/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */

package io.iec.edp.caf.multicontext.context;

import io.iec.edp.caf.commons.runtime.CafEnvironment;
import io.iec.edp.caf.commons.runtime.config.ServiceUnitConfigService;
import io.iec.edp.caf.multicontext.ModuleBootstrap;
import io.iec.edp.caf.multicontext.analysis.StartupAnalysisBeanPostProcesser;
import io.iec.edp.caf.multicontext.analysis.StartupAnalysisLog;
import io.iec.edp.caf.multicontext.classloader.ModuleClassloader;
import io.iec.edp.caf.multicontext.classloader.PlatformClassloader;
import io.iec.edp.caf.multicontext.config.ParallelConfigReader;
import io.iec.edp.caf.multicontext.config.ParallelSetting;
import io.iec.edp.caf.multicontext.config.SpecialModule;
import io.iec.edp.caf.multicontext.exception.ExceptionHandledThreadPool;
import io.iec.edp.caf.multicontext.exception.ParallelStartUpException;
import io.iec.edp.caf.multicontext.factory.AspectJFactory;
import io.iec.edp.caf.multicontext.factory.ModuleBeanFactory;
import io.iec.edp.caf.multicontext.factory.PlatformBeanFactory;
import io.iec.edp.caf.multicontext.hibernate.CustomHibernatePropertiesCustomizer;
import io.iec.edp.caf.multicontext.resolver.MultiContextResourceResolver;
import io.iec.edp.caf.multicontext.support.*;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.config.AopConfigUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.domain.EntityScanPackages;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
import org.springframework.boot.loader.LaunchedURLClassLoader;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.support.ResourcePatternResolver;

import sun.misc.URLClassPath;

import java.lang.annotation.Annotation;

import java.net.URL;
import java.util.*;
import java.util.concurrent.*;


//todo 异常处理 生命周期管理
/**
 * 平台底座ApplicationContext
 * <p>
 * <p>
 * todo 自定义线程池 实现线程绑定
 * todo 分级处理
 *
 * parallel:
 *   base:
 *     paths:
 *     - platform/common
 *     - platform/runtime/common
 *     - platform/runtime/sys
 *   module:
 *     principle: keyApp
 *     includes:
 *     - apps
 *     - platform/runtime
 *     - platform/dev
 *     excludes:
 *     - platform/runtime/cloudprint
 *     special-modules:
 *     - name: tmhb
 *       includes:
 *       - apps/tm
 *       - apps/ihc
 *     special-beans:
 *     - wfTaskServerEndpointExporter
 *
 * @author guowenchang
 */
@Slf4j
public class PlatformApplicationContext extends AnnotationConfigServletWebServerApplicationContext {

    private final ModuleManager moduleManager;
    private final Logger logger = LoggerFactory.getLogger(PlatformApplicationContext.class);
    private String basePath;

    //模块用的线程池 todo 异常管理
    //private final ExecutorService executor = Executors.newFixedThreadPool(64);
    private final ExceptionHandledThreadPool executor = new ExceptionHandledThreadPool(64,64,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>());
    private final int size;

    //缓存的已加载的ServerEndpoint的bean名称
    private static Map<String,String> registedServerEndpoint = new HashMap<>();




    /**
     * 通过类注解来获取含有该注解的Bean名称
     * WebSocket注册ServerEndpoint时会调用此方法
     * 重写此方法获取各个moduleContext的Bean
     * @param annotationType annotationType
     * @return beanNames
     */
    @Override
    public String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType) {
        String[] resultPlat = super.getBeanNamesForAnnotation(annotationType);
        int lenth = 0;
        Map<String,String> map = new HashMap<>();
        for(String result:resultPlat){
            if(!map.containsKey(result)){
                map.put(result,result);
                lenth++;
            }
        }
        //须获取所有注解了ServerEndpoint时，遍历各个模块
        if(annotationType.getName().contains("ServerEndpoint")){
            Iterator iterator = moduleManager.iterator();
            while (iterator.hasNext()){
                Module module = (Module)iterator.next();
                //确保该模块Context的bean已经初始化完成
                if(module.getContext().isActive()){
                    //根据注解获取该模块的beanName
                    String[] resultMod = module.getContext().getBeanNamesForAnnotation(annotationType);
                    for(String result:resultMod){
                        //因为加载时机有的时候会重复加载导致启动失败，这里将已加载的缓存下来
                        if(!map.containsKey(result)&&!registedServerEndpoint.containsKey(result)){
                            map.put(result,result);
                            registedServerEndpoint.put(result,result);
                            lenth++;
                            log.info("当前正在注册ServerEndpoint的bean："+result);
                        }
                    }
                }

            }
        }

        String[] all = new String[lenth];
        int i = 0;
        for(String key:map.keySet()){
            all[i] = key;
            i++;
        }
        return all;
    }


    //SpringApplication在run的时候通过this.createApplicationContext()来创建这个PlatformApplicationContext
    public PlatformApplicationContext() {
        super(new PlatformBeanFactory(new ModuleManager()));

        LocalThreadModule.setMultiContext(true);
        //通过线程上下文获取到ClassLoader
        PlatformClassloader platformClassloader = (PlatformClassloader) Thread.currentThread().getContextClassLoader();

        //准备环境 公有资源
        PlatformBeanFactory beanFactory = (PlatformBeanFactory) getBeanFactory();

        StartupAnalysisLog.setApplicationContext(this);

        this.moduleManager = beanFactory.getModuleManager();
        this.moduleManager.setInternalClassloader((LaunchedURLClassLoader) platformClassloader.getParent());

        this.basePath = CafEnvironment.getServerRTPath() + "/";
        //获取底座及各个模块的目录
        ParallelSetting setting = ParallelConfigReader.readParallelSetting();

        List<String> platformPaths = setting.getBasePaths();

        /**
         * 将底座目录加入环境
         * 在 @PlatformApplicationListener 中已将底座目录加入环境
         * 为防listenner没有加载到的情况，仍然保留逻辑
         */
        platformPaths.forEach(url -> {
            addPlatformUrl(platformClassloader, JarUtil.getJarPathFromDirPaths(basePath + url));
            logger.info("底座目录：" + url);
        });

        //拆分子目录
        List<SpecialModule> modules = ModuleSplittingCalculator.calculate(setting);
        //获取所有已启用的su
        List<String> enableSu = ServiceUnitConfigService.getEnableSu();
        log.info("enable service units: " + String.join(",",enableSu));
        //初始化模块
        for (SpecialModule module : modules) {
            //存储当前模块的所有su路径信息
            List<String> paths = new ArrayList<>();

            //为了预热时能获取su对应的url
            Map<String, URLClassPath> appClassPath = platformClassloader.getAppClasspath();
            if(module.getSuMapping()!=null){
                module.getSuMapping().forEach((key,value)->{
                    URL[] urls = JarUtil.getJarPathFromDirPaths(value);
                    //之后将所有启用的su所在的路径加入模块classloader中
                    for(String su:enableSu){
                        if(su.equalsIgnoreCase(key)){
                            paths.add((String)value);
                            //只将启用的su加入appClasspath中
                            if(!appClassPath.containsKey(key.toLowerCase())){
                                appClassPath.put(key.toLowerCase(),new URLClassPath(urls));
                            }
                            break;
                        }
                    }
                });
            }else{
                log.info("Module: "+module.getName()+" doesn't have any ServiceUnits");
                //如果当前模块没有ServiceUnit.json（没有su），则不加载当前模块
                continue;
            }

            //如果当前模块没有可用的su，则不加载当前模块
            if(paths.size()==0){
                log.info("Module: "+module.getName()+" doesn't have any enable ServiceUnits");
                continue;
            }

            //构造ModuleClassloader时，一是会把module里的所有jar的url加到platformClassloader里，同时给自己创建一个resourceLoader处理自己的资源
            ClassLoader classLoader = new ModuleClassloader(JarUtil.getJarPathFromPaths(paths), platformClassloader);

            ModuleBeanFactory moduleBeanFactory = new ModuleBeanFactory(beanFactory,module.getName());
            moduleBeanFactory.addBeanPostProcessor(new StartupAnalysisBeanPostProcesser(module.getName()));

            moduleBeanFactory.setBeanClassLoader(classLoader);
            ModuleApplicationContext moduleApplicationContext = new ModuleApplicationContext(moduleBeanFactory);
            moduleApplicationContext.setClassLoader(classLoader);
            moduleApplicationContext.setParent(this);
            moduleApplicationContext.setEnvironment(this.getEnvironment());
            moduleApplicationContext.addBeanFactoryPostProcessor(new ModuleMetadataReaderFactoryPostProcessor());
            moduleApplicationContext.addBeanFactoryPostProcessor(new BeanCollectorBeanFactoryPostProcessor());
            logger.info(module.getName());
            moduleManager.addModule(new Module(module.getName(), classLoader, module.getIncludes(), moduleApplicationContext, moduleBeanFactory));

            //为每一个ModuleContext注册一个启动类，并指定Transaction属性以生成代理类
            new AnnotatedBeanDefinitionReader(moduleApplicationContext).register(ModuleBootstrap.class);
            CustomHibernatePropertiesCustomizer.addClassloader(classLoader);
            //AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(moduleApplicationContext);
        }

        size = moduleManager.size();

        //此前已经读取并生成了spring.factories的缓存 这里要清除一下
        SpringFactoriesOperator.clearSpringFactoriesCache();

        //这一句是为了把AspectJ的Processor注册进去
        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(this);
        //添加Bean收集类处理器
        this.addBeanFactoryPostProcessor(new BeanCollectorBeanFactoryPostProcessor());
        this.getBeanFactory().addBeanPostProcessor(new StartupAnalysisBeanPostProcesser("platform"));
    }

    @Override
    protected ConfigurableEnvironment createEnvironment() {
        //拷贝environment，支持ConditionalOnExpression，同时避免模块中因排除的AutoConfiguration列表出错
        ConfigurableEnvironment env = (ConfigurableEnvironment)CafEnvironment.getEnvironment();
        ConfigurableEnvironment environment = new StandardEnvironment();
        env.getPropertySources().forEach(propertySource -> {
            if(propertySource instanceof OriginTrackedMapPropertySource ||
                    propertySource.getName().equalsIgnoreCase("systemProperties")){
                environment.getPropertySources().addLast(propertySource);
            }
        });
        return environment;
    }


    private void addPlatformUrl(PlatformClassloader classloader, URL[] urls) {
        classloader.addPlatformURL(urls);
    }

    public ModuleManager getModuleManager() {
        return moduleManager;
    }

    @Override
    protected ResourcePatternResolver getResourcePatternResolver() {
        return new MultiContextResourceResolver(this);
    }

    /**
     * 生命周期部分
     * <p> todo 异常处理
     * 先初始化平台context
     * 再并发初始化各个module的context
     */
    @Override
    protected void prepareRefresh() {
        long time = System.currentTimeMillis();
        super.prepareRefresh();
        logger.info("platform prepareRefresh total time: " + (System.currentTimeMillis()-time) + "ms");

        CountDownLatch countDownLatch = new CountDownLatch(this.size);

        for (Module module : moduleManager) {
            ModuleApplicationContext context = module.getContext();
            executor.execute(countDownLatchWrap(() -> context.prepareRefresh(), countDownLatch, module, "prepareRefresh"));
        }

        try {
            countDownLatch.await();
            if(executor.getExceptions().size()>0){
                throw new ParallelStartUpException("并行启动过程中出现错误。",executor.getExceptions().get(0));
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        long cost = System.currentTimeMillis() - time;
        logger.info("prepareRefresh total time: " + cost + "ms");
    }

    @Override
    protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        long time = System.currentTimeMillis();
        super.prepareBeanFactory(beanFactory);
        logger.info("platform prepareBeanFactory total time: " + (System.currentTimeMillis()-time) + "ms");

        CountDownLatch countDownLatch = new CountDownLatch(this.size);

        for (Module module : moduleManager) {
            ModuleApplicationContext context = module.getContext();
            executor.execute(countDownLatchWrap(() -> context.prepareBeanFactory(module.getName()), countDownLatch, module, "prepareBeanFactory"));
        }

        try {
            countDownLatch.await();
            if(executor.getExceptions().size()>0){
                throw new ParallelStartUpException("并行启动过程中出现错误。",executor.getExceptions().get(0));
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        long cost = System.currentTimeMillis() - time;
        logger.info("prepareBeanFactory total time: " + cost + "ms");
    }

    @Override
    protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        long time = System.currentTimeMillis();
        super.postProcessBeanFactory(beanFactory);
        logger.info("platform postProcessBeanFactory total time: " + (System.currentTimeMillis()-time) + "ms");

        CountDownLatch countDownLatch = new CountDownLatch(this.size);

        for (Module module : moduleManager) {
            ModuleApplicationContext context = module.getContext();
            executor.execute(countDownLatchWrap(() -> context.postProcessBeanFactory(), countDownLatch, module, "postProcessBeanFactory"));
        }

        try {
            countDownLatch.await();
            if(executor.getExceptions().size()>0){
                throw new ParallelStartUpException("并行启动过程中出现错误。",executor.getExceptions().get(0));
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        long cost = System.currentTimeMillis() - time;
        logger.info("postProcessBeanFactory total time: " + cost + "ms");
    }

    @Override
    protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
        long time = System.currentTimeMillis();
        super.invokeBeanFactoryPostProcessors(beanFactory);
        logger.info("platform invokeBeanFactoryPostProcessors total time: " + (System.currentTimeMillis()-time) + "ms");

        //在这个阶段要取这个Creator出来替换里边的AspectJFactory 并且把里边的beanFactory换掉
        //下面两行最初是为了解决之前因为类加载器不一致而出现的切面报错，后来把类加载器改成同一个之后 应该可以去掉了，但是怕出问题就没验证
        //todo 抽时间验证看看是否可以去掉
        AnnotationAwareAspectJAutoProxyCreator jAutoProxyCreator = this.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
        jAutoProxyCreator.setAspectJAdvisorFactory(new AspectJFactory());
        jAutoProxyCreator.setBeanFactory(this.getBeanFactory());

        CountDownLatch countDownLatch = new CountDownLatch(this.size);

        for (Module module : moduleManager) {
            ModuleApplicationContext context = module.getContext();
            executor.execute(countDownLatchWrap(() -> context.invokeBeanFactoryPostProcessors(), countDownLatch, module, "invokeBeanFactoryPostProcessors"));
        }

        try {
            countDownLatch.await();
            if(executor.getExceptions().size()>0){
                throw new ParallelStartUpException("并行启动过程中出现错误。",executor.getExceptions().get(0));
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        long cost = System.currentTimeMillis() - time;
        logger.info("invokeBeanFactoryPostProcessors total time: " + cost + "ms");

        //处理EntityScanPackages
        mergeEntityScanPackages();
    }

    @SneakyThrows
    private void mergeEntityScanPackages() {
        List<String> packages = new ArrayList<>();

        for (Module module : moduleManager) {
            BeanDefinition beanDefinition;
            try {
                beanDefinition = module.getBeanFactory().getBeanDefinition(EntityScanPackages.class.getName());
            } catch (NoSuchBeanDefinitionException e) {
                continue;
            }


            /**
             * 因为升级boot到2.4.13，导致以前的EntityScanPackages是记录在constructorArguments上，现在记录到了
             * EntityScanPackages的内部类的EntityScanPackagesBeanDefinition的packageNames上
             */
            var declaredClass = EntityScanPackages.class.getDeclaredClasses();
            for(var cls:declaredClass){
                if(cls.getName().equalsIgnoreCase("org.springframework.boot.autoconfigure.domain.EntityScanPackages$EntityScanPackagesBeanDefinition")){
                    var field = cls.getDeclaredField("packageNames");
                    field.setAccessible(true);
                    var packageset = (Set<String>) field.get(beanDefinition);
                    for (var s:packageset){
                        packages.add(s);
                    }
                    break;
                }
            }
//            ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
//            if(constructorArguments.getArgumentValue(0, String[].class)!=null){
//                String[] value = (String[]) constructorArguments.getArgumentValue(0, String[].class).getValue();
//                packages.addAll(Arrays.asList(value));
//                module.getBeanFactory().removeBeanDefinition(EntityScanPackages.class.getName());
//            }
        }

        EntityScanPackages.register((BeanDefinitionRegistry) this.getBeanFactory(), packages);
    }

    @Override
    protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
        long time = System.currentTimeMillis();
        super.registerBeanPostProcessors(beanFactory);
        logger.info("platform registerBeanPostProcessors total time: " + (System.currentTimeMillis()-time) + "ms");

        CountDownLatch countDownLatch = new CountDownLatch(this.size);

        var platformBeanPostProcesser = ((PlatformBeanFactory)beanFactory).getBeanPostProcessors();

        for (Module module : moduleManager) {
            ModuleApplicationContext context = module.getContext();
//            executor.execute(countDownLatchWrap(() -> context.registerBeanPostProcessors(null), countDownLatch, module, "registerBeanPostProcessors"));
            executor.execute(countDownLatchWrap(() -> {
                /**
                 * 模块自身注册BeanPostProcessors之后先把platformacontext里的一些属于我们自己的beanpostprocesser也注册到模块里
                 * 否则的话模块里的bean会失去这些beanpostprocessor的时机
                 *
                 * 该问题的发现点：
                 *  1.tcc那边的GlobalTransactionScanner的这个beanpostprocessor在module里没有触发
                 */

                context.registerBeanPostProcessors(platformBeanPostProcesser);
                context.afterRegisterBeanPostProcessors(platformBeanPostProcesser);
            }, countDownLatch, module, "registerBeanPostProcessors"));
        }

        try {
            countDownLatch.await();
            if(executor.getExceptions().size()>0){
                throw new ParallelStartUpException("并行启动过程中出现错误。",executor.getExceptions().get(0));
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        long cost = System.currentTimeMillis() - time;
        logger.info("registerBeanPostProcessors total time: " + cost + "ms");
    }

    @Override
    protected void initMessageSource() {
        long time = System.currentTimeMillis();
        super.initMessageSource();
        logger.info("platform initMessageSource total time: " + (System.currentTimeMillis()-time) + "ms");

        CountDownLatch countDownLatch = new CountDownLatch(this.size);

        for (Module module : moduleManager) {
            ModuleApplicationContext context = module.getContext();
            executor.execute(countDownLatchWrap(() -> context.initMessageSource(), countDownLatch, module, "initMessageSource"));
        }

        try {
            countDownLatch.await();
            if(executor.getExceptions().size()>0){
                throw new ParallelStartUpException("并行启动过程中出现错误。",executor.getExceptions().get(0));
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        long cost = System.currentTimeMillis() - time;
        logger.info("initMessageSource total time: " + cost + "ms");
    }

    @Override
    protected void initApplicationEventMulticaster() {
        long time = System.currentTimeMillis();
        super.initApplicationEventMulticaster();
        logger.info("platform initApplicationEventMulticaster total time: " + (System.currentTimeMillis()-time) + "ms");

        CountDownLatch countDownLatch = new CountDownLatch(this.size);

        for (Module module : moduleManager) {
            ModuleApplicationContext context = module.getContext();
            executor.execute(countDownLatchWrap(() -> context.initApplicationEventMulticaster(), countDownLatch, module, "initApplicationEventMulticaster"));
        }

        try {
            countDownLatch.await();
            if(executor.getExceptions().size()>0){
                throw new ParallelStartUpException("并行启动过程中出现错误。",executor.getExceptions().get(0));
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        long cost = System.currentTimeMillis() - time;
        logger.info("initApplicationEventMulticaster total time: " + cost + "ms");
    }

    @Override
    protected void onRefresh() {
        long time = System.currentTimeMillis();
//        long logTime = System.currentTimeMillis();
//        long logCost = System.currentTimeMillis()-logTime;

        super.onRefresh();
        logger.info("platform onRefresh total time: " + (System.currentTimeMillis()-time) + "ms");

        CountDownLatch countDownLatch = new CountDownLatch(this.size);

        for (Module module : moduleManager) {
            ModuleApplicationContext context = module.getContext();
            executor.execute(countDownLatchWrap(() -> {
                LocalThreadModule.setModule(module);
                context.onRefresh();
                LocalThreadModule.purgeModule();
            }, countDownLatch, module, "onRefresh"));

//            logTime = System.currentTimeMillis();
            StartupAnalysisLog.countBeanMap(module.getName(),module.getBeanFactory());
//            logCost += (System.currentTimeMillis()-logTime);
        }

        try {
            countDownLatch.await();
            if(executor.getExceptions().size()>0){
                throw new ParallelStartUpException("并行启动过程中出现错误。",executor.getExceptions().get(0));
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        long cost = System.currentTimeMillis() - time;
        logger.info("onRefresh total time: " + cost+ "ms");

        StartupAnalysisLog.countBeanMap("platform",this.getBeanFactory());

    }

    @Override
    protected void registerListeners() {
        long time = System.currentTimeMillis();
        super.registerListeners();
        logger.info("platform registerListeners total time: " + (System.currentTimeMillis()-time) + "ms");


        CountDownLatch countDownLatch = new CountDownLatch(this.size);

        for (Module module : moduleManager) {
            ModuleApplicationContext context = module.getContext();
            executor.execute(countDownLatchWrap(() -> {
                context.registerListeners();

//                特殊处理了下CAF的启动后事件，因为CAF的启动后事件是在PlatformApplicationContext里触发的。
//                但是子模块里注册的这个事件的Bean都不在PlatformApplicationContext里，所以这里把这些CAF启动后事件的bean识别出来注册到PlatformApplicationContext里
//                todo 这里最好是想办法让各个模块去触发自己的事件？这样把子模块的bean弄过来有污染
                context.afterRegisterListeners(this);

            }, countDownLatch, module, "prepareRefresh"));
        }

        try {
            countDownLatch.await();
            if(executor.getExceptions().size()>0){
                throw new ParallelStartUpException("并行启动过程中出现错误。",executor.getExceptions().get(0));
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        long cost = System.currentTimeMillis() - time;
        logger.info("registerListeners total time: " + cost + "ms");

    }

    @Override
    protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
        long time = System.currentTimeMillis();
        super.finishBeanFactoryInitialization(beanFactory);
        long cost = System.currentTimeMillis() - time;
        logger.info("platform finishBeanFactoryInitialization cost:" + cost + "ms");

        CountDownLatch countDownLatch = new CountDownLatch(this.size);

        for (Module module : moduleManager) {
            ModuleApplicationContext context = module.getContext();
            executor.execute(countDownLatchWrap(() -> {
                LocalThreadModule.setModule(module);
                context.finishBeanFactoryInitialization();
                LocalThreadModule.purgeModule();
            }, countDownLatch, module, "finishBeanFactoryInitialization"));
        }
        try {
            countDownLatch.await();
            if(executor.getExceptions().size()>0){
                throw new ParallelStartUpException("并行启动过程中出现错误。",executor.getExceptions().get(0));
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        for (BeanFactoryPostProcessor beanFactoryPostProcessor : this.getBeanFactoryPostProcessors()) {
            if (beanFactoryPostProcessor instanceof BeanCollectorBeanFactoryPostProcessor) {
                ((BeanCollectorBeanFactoryPostProcessor) beanFactoryPostProcessor).instantiationBeanCollectors();
            }
        }

        cost = System.currentTimeMillis() - time;
        logger.info("finishBeanFactoryInitialization total time: " + cost + "ms");
    }

    @Override
    protected void finishRefresh() {
        long time = System.currentTimeMillis();
        super.finishRefresh();
        logger.info("platform finishRefresh total time: " + (System.currentTimeMillis()-time) + "ms");

        CountDownLatch countDownLatch = new CountDownLatch(this.size);

        for (Module module : moduleManager) {
            var beanName = module.getName();
            var beanCounts = module.getBeanFactory().getBeanDefinitionCount();
            StartupAnalysisLog.countBeanCountMap(beanName,beanCounts);

            ModuleApplicationContext context = module.getContext();
            executor.execute(countDownLatchWrap(() -> context.finishRefresh(), countDownLatch, module, "finishRefresh"));
        }

        StartupAnalysisLog.countBeanCountMap("platform",this.getBeanFactory().getBeanDefinitionCount());

        try {
            countDownLatch.await();
            if(executor.getExceptions().size()>0){
                throw new ParallelStartUpException("并行启动过程中出现错误。",executor.getExceptions().get(0));
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        long cost = System.currentTimeMillis() - time;
        logger.info("finishRefresh total time: " + cost + "ms");
    }

    @Override
    protected void destroyBeans() {
        long time = System.currentTimeMillis();

        super.destroyBeans();

        CountDownLatch countDownLatch = new CountDownLatch(this.size);

        for (Module module : moduleManager) {
            ModuleApplicationContext context = module.getContext();
            executor.execute(countDownLatchWrap(() -> context.destroyBeans(), countDownLatch, module, "destroyBeans"));
        }

        try {
            countDownLatch.await();
            if(executor.getExceptions().size()>0){
                throw new ParallelStartUpException("并行启动过程中出现错误。",executor.getExceptions().get(0));
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long cost = System.currentTimeMillis() - time;
        logger.info("destroyBeans total time: " + cost + "ms");

    }

    @Override
    protected void cancelRefresh(BeansException ex) {
        long time = System.currentTimeMillis();

        super.cancelRefresh(ex);

        CountDownLatch countDownLatch = new CountDownLatch(this.size);

        for (Module module : moduleManager) {
            ModuleApplicationContext context = module.getContext();
            executor.execute(countDownLatchWrap(() -> context.cancelRefresh(ex), countDownLatch, module, "cancelRefresh"));
        }

        try {
            countDownLatch.await();
            if(executor.getExceptions().size()>0){
                throw new ParallelStartUpException("并行启动过程中出现错误。",executor.getExceptions().get(0));
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long cost = System.currentTimeMillis() - time;
        logger.info("cancelRefresh total time: " + cost + "ms");
    }

    @Override
    protected void resetCommonCaches() {
        long time = System.currentTimeMillis();

        CountDownLatch countDownLatch = new CountDownLatch(this.size);

        for (Module module : moduleManager) {
            ModuleApplicationContext context = module.getContext();
            executor.execute(countDownLatchWrap(() -> context.resetCommonCaches(), countDownLatch, module, "resetCommonCaches"));
        }

        try {
            countDownLatch.await();
            if(executor.getExceptions().size()>0){
                throw new ParallelStartUpException("并行启动过程中出现错误。",executor.getExceptions().get(0));
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        super.resetCommonCaches();
        long cost = System.currentTimeMillis() - time;
        logger.info("resetCommonCaches total time: " + cost + "ms");
    }

    /**
     * 包装Runnable方法 等待countDownLatch
     *
     * @param runnable
     * @param countDownLatch
     * @param stage
     * @return
     */
    private Runnable countDownLatchWrap(Runnable runnable, CountDownLatch countDownLatch, Module module, String stage) {
        return () -> {
            long time = System.currentTimeMillis();
            try {
                runnable.run();
            } catch (Exception e) {
                logger.error("Failed to start " + module.getName()+":"+stage, e);
                String parallelMode = this.getEnvironment().getProperty("parallel.mode","start");
                if(parallelMode.equals("check")){
                    throw new ParallelStartUpException(e);
                }
            } finally {
                long cost = System.currentTimeMillis() - time;
                StartupAnalysisLog.countBeanStepTime(module.getName(),stage,cost);
                countDownLatch.countDown();
                logger.info(module.getName() + " " + stage + " time: " + cost + "ms");
            }
        };
    }
}
